package com.tsfyun.scm.service.impl.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tsfyun.common.base.dto.TransactionFlowDTO;
import com.tsfyun.common.base.enums.DistributedLockEnum;
import com.tsfyun.common.base.enums.exp.CollectionSourceEnum;
import com.tsfyun.common.base.enums.finance.TransactionCategoryEnum;
import com.tsfyun.common.base.enums.finance.TransactionTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.common.base.help.excel.ExcelUtil;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.util.LocalDateTimeUtils;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.common.base.validator.ValidatorUtils;
import com.tsfyun.scm.dto.order.CostChangeRecordExpDTO;
import com.tsfyun.scm.dto.order.CostChangeRecordExpQTO;
import com.tsfyun.scm.dto.order.ImportCostChangeRecordExpDTO;
import com.tsfyun.scm.entity.order.*;
import com.tsfyun.scm.entity.system.ExpenseSubject;
import com.tsfyun.scm.excel.ImportExpOrderCostExcel;
import com.tsfyun.scm.excel.ImportExpOrderOtherCostExcel;
import com.tsfyun.scm.mapper.order.CostChangeRecordExpMapper;
import com.tsfyun.scm.service.base.ICurrencyService;
import com.tsfyun.scm.service.finance.ITransactionFlowService;
import com.tsfyun.scm.service.finance.IWriteOffService;
import com.tsfyun.scm.service.order.ICostChangeRecordExpService;
import com.tsfyun.scm.service.order.IExpOrderCostService;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.system.IExpenseSubjectService;
import com.tsfyun.scm.util.ExpenseSubjectUtil;
import com.tsfyun.scm.vo.base.CurrencyVO;
import com.tsfyun.scm.vo.order.CostChangeRecordExpVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
import java.util.stream.Collectors;

@RefreshScope
@Slf4j
@Service
public class CostChangeRecordExpServiceImpl extends ServiceImpl<CostChangeRecordExp> implements ICostChangeRecordExpService {

    @Autowired
    private CostChangeRecordExpMapper costChangeRecordExpMapper;
    @Autowired
    private IExpenseSubjectService expenseSubjectService;
    @Autowired
    private RedisLockRegistry redisLockRegistry;
    @Value("${redis.lock.time:5}")
    private Integer lockTime;
    @Autowired
    private IExpOrderService expOrderService;
    @Autowired
    private IExpOrderCostService expOrderCostService;
    @Autowired
    private IWriteOffService writeOffService;
    @Autowired
    private ITransactionFlowService transactionFlowService;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private ICurrencyService currencyService;

    @Override
    public PageInfo<CostChangeRecordExpVO> pageList(CostChangeRecordExpQTO qto) {
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        List<CostChangeRecordExpVO> list = costChangeRecordExpMapper.list(params);
        return new PageInfo<>(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByOrderId(Long orderId) {
        costChangeRecordExpMapper.removeByOrderId(orderId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void adjustOrderCost(CostChangeRecordExpDTO dto) {
        String lockKey = DistributedLockEnum.ORDER_COST_ADJUST.getCode() + ":" + dto.getExpOrderNo();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            isLock = lock.tryLock(lockTime, TimeUnit.SECONDS);
            if (!isLock) {
                log.error(String.format("未获取到锁，订单号：【%s】", dto.getExpOrderNo()));
                throw new ServiceException("服务拥挤，请稍后再试");
            }
            //获取所有已启用的费用科目信息，key为费用科目id（读缓存）
            ExpenseSubject expenseSubject = expenseSubjectService.getById(dto.getExpenseSubjectId());
            Optional.ofNullable(expenseSubject).orElseThrow(()->new ServiceException("费用科目错误"));
            ExpOrder expOrder = expOrderService.findByDocNo(dto.getExpOrderNo());
            Optional.ofNullable(expOrder).orElseThrow(()->new ServiceException(StrUtil.format("订单号{}错误",dto.getExpOrderNo())));
            TsfPreconditions.checkArgument(StringUtils.isEmpty(expOrder.getPurchaseContractNo()),new ServiceException(StrUtil.format("订单{}已经生成采购合同，不允许调整费用",dto.getExpOrderNo())));
            BigDecimal originCostAmount = BigDecimal.ZERO;
            ExpOrderCost expOrderCost;
            if(Objects.nonNull(dto.getExpOrderCostId())) {
                // 修改费用
                expOrderCost = expOrderCostService.getById(dto.getExpOrderCostId());
                Optional.ofNullable(expOrderCost).orElseThrow(()->new ServiceException("费用信息不存在"));
                //修改订单费用不允许修改费用科目
                TsfPreconditions.checkArgument(Objects.equals(expOrderCost.getExpenseSubjectId(),dto.getExpenseSubjectId()),new ServiceException(StrUtil.format("修改订单{}费用不允许修改费用科目【{}】",dto.getExpOrderNo(),expenseSubject.getName())));
                //校验订单费用信息与订单是否匹配
                TsfPreconditions.checkArgument(Objects.equals(expOrderCost.getExpOrderId().toString(),expOrder.getId().toString()),new ServiceException(StrUtil.format("订单{}费用【{}】与订单不匹配",dto.getExpOrderNo(),expenseSubject.getName())));
                //检查是否调整了费用（前端也需要做验证）
                if(expOrderCost.getCollectionSource().equals(dto.getCollectionSource())){
                    if(expOrderCost.getReceAmount().compareTo(dto.getCostAmount()) == 0){
                        throw new ServiceException(StrUtil.format("订单{}费用【{}】已经存在，费用没有变更",dto.getExpOrderNo(),expenseSubject.getName()));
                    }
                }
                TsfPreconditions.checkArgument(Objects.equals(expOrderCost.getIsAllowEdit(),Boolean.TRUE),new ServiceException(StrUtil.format("订单{}费用【{}】不允许修改",dto.getExpOrderNo(),expenseSubject.getName())));
                // 检查原支付方式是否(客户单独支付)
                if(Objects.equals(CollectionSourceEnum.PAYMENT,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ||
                        Objects.equals(CollectionSourceEnum.OTHER,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ){
                    // 检查费用是否核销
                    if(expOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO) == 1){
                        //取消核销
                        writeOffService.cancelWriteOffByExpOrderCost(expOrderCost);
                    }
                    // 处理交易流水
                    if(Objects.equals(Boolean.TRUE,expOrderCost.getIsLock())) {
                        TransactionFlowDTO param = new TransactionFlowDTO();
                        param.setCustomerId(expOrder.getCustomerId());
                        param.setTransactionType(TransactionTypeEnum.EXP_ORDER.getCode());
                        param.setTransactionNo(expOrder.getDocNo());
                        param.setMemo(String.format("出口费用调整取消：%s[%s]",expenseSubject.getName(),expOrderCost.getReceAmount()));
                        param.setAmount(expOrderCost.getReceAmount());
                        param.setTransactionCategory(TransactionCategoryEnum.INCOME.getCode());
                        transactionFlowService.recordTransactionFlow(param);
                    }
                }
                //获取订单修改前费用
                originCostAmount = expOrderCost.getReceAmount();
                expOrderCost.setActualAmount(originCostAmount);
                //应收金额
                expOrderCost.setReceAmount(dto.getCostAmount());
                expOrderCost.setMemo(dto.getMemo());
                //防止系统重复计算
                expOrderCost.setIsAutomatic(Boolean.FALSE);
                expOrderCost.setOperator(SecurityUtil.getCurrentPersonName());
                //计算账期
                expOrderCost.setHappenDate(expOrder.getOrderDate());
                expOrderCost.setPeriodDate(expOrder.getOrderDate());
                expOrderCost.setLateFeeDate(expOrder.getOrderDate());
                expOrderCost.setOverdueRate(BigDecimal.ZERO);
                // 支付方式
                expOrderCost.setCollectionSource(dto.getCollectionSource());
                if("A0001".equals(expOrderCost.getExpenseSubjectId()) && !Objects.equals(CollectionSourceEnum.of(dto.getCollectionSource()),CollectionSourceEnum.DEDUCTION)){
                    throw new ServiceException(StrUtil.format("订单{}代理费支付方式必须为【{}】",dto.getExpOrderNo(),CollectionSourceEnum.DEDUCTION.getName()));
                }
                expOrderCostService.updateById(expOrderCost);
            }else{
                // 新增费用
                // 需判断订单是否存在该科目的费用
                List<ExpOrderCost> existsExpOrderCost = expOrderCostService.getByExpenseSubject(expOrder.getId(),expenseSubject.getId());
                TsfPreconditions.checkArgument(CollUtil.isEmpty(existsExpOrderCost),new ServiceException(StrUtil.format("订单{}已经存在科目为【{}】的费用信息，您可以直接修改。",dto.getExpOrderNo(),expenseSubject.getName())));
                expOrderCost = new ExpOrderCost();
                expOrderCost.setExpenseSubjectId(expenseSubject.getId());
                expOrderCost.setExpOrderId(expOrder.getId());
                expOrderCost.setCostClassify(ExpenseSubjectUtil.getCostClassify(expenseSubject.getId()));
                expOrderCost.setIsAutomatic(Boolean.FALSE);
                expOrderCost.setIsFirstWriteOff(Boolean.FALSE);
                expOrderCost.setIsLock(expOrder.getIsExp());
                //计算账期
                expOrderCost.setHappenDate(expOrder.getOrderDate());
                expOrderCost.setPeriodDate(expOrder.getOrderDate());
                expOrderCost.setLateFeeDate(expOrder.getOrderDate());
                expOrderCost.setOverdueRate(BigDecimal.ZERO);
                expOrderCost.setMark(expOrder.getDocNo());
                expOrderCost.setOperator(SecurityUtil.getCurrentPersonName());
                expOrderCost.setIsAllowEdit(Boolean.TRUE);//人工新增或修改的可以修改
                //原始费用
                expOrderCost.setActualAmount(BigDecimal.ZERO);
                //应收金额
                expOrderCost.setReceAmount(dto.getCostAmount());
                expOrderCost.setMemo(dto.getMemo());
                // 支付方式
                expOrderCost.setCollectionSource(dto.getCollectionSource());
                if("A0001".equals(expOrderCost.getExpenseSubjectId()) && !Objects.equals(CollectionSourceEnum.of(dto.getCollectionSource()),CollectionSourceEnum.DEDUCTION)){
                    throw new ServiceException(StrUtil.format("订单{}代理费支付方式必须为【{}】",dto.getExpOrderNo(),CollectionSourceEnum.DEDUCTION.getName()));
                }
                expOrderCostService.saveNonNull(expOrderCost);
            }

            //登记费用调整记录
            CostChangeRecordExp costChangeRecord = new CostChangeRecordExp();
            costChangeRecord.setExpOrderId(expOrder.getId());
            costChangeRecord.setExpOrderNo(expOrder.getDocNo());
            costChangeRecord.setCustomerId(expOrder.getCustomerId());
            costChangeRecord.setExpenseSubjectId(expenseSubject.getId());
            costChangeRecord.setExpenseSubjectName(expenseSubject.getName());
            costChangeRecord.setOriginCostAmount(originCostAmount);
            costChangeRecord.setCostAmount(expOrderCost.getReceAmount());
            costChangeRecord.setMemo(expOrderCost.getMemo());
            super.saveNonNull(costChangeRecord);

            // 修改后支付方式是否(客户单独支付)
            if(Objects.equals(CollectionSourceEnum.PAYMENT,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ||
                    Objects.equals(CollectionSourceEnum.OTHER,CollectionSourceEnum.of(expOrderCost.getCollectionSource()))){
                // 处理交易流水
                if(Objects.equals(Boolean.TRUE,expOrderCost.getIsLock())) {
                    TransactionFlowDTO param = new TransactionFlowDTO();
                    param.setCustomerId(expOrder.getCustomerId());
                    param.setTransactionType(TransactionTypeEnum.EXP_ORDER.getCode());
                    param.setTransactionNo(expOrder.getDocNo());
                    param.setMemo(String.format("出口费用调整增加：%s[%s]",expenseSubject.getName(),expOrderCost.getReceAmount()));
                    param.setAmount(expOrderCost.getReceAmount());
                    param.setTransactionCategory(TransactionCategoryEnum.OUTCOME.getCode());
                    transactionFlowService.recordTransactionFlow(param);

                    //执行订单费用核销
                    writeOffService.writeOffExpOrderId(expOrder.getId());
                }
            }
        }  catch (InterruptedException e) {
            log.error("费用调整获取锁异常",e);
            throw new ServiceException("服务拥挤，请稍后再试");
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importOrderCost(MultipartFile file) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        //验证导入数据是否符合规范
        List<ImportCostChangeRecordExpDTO> costChangeRecordExpDTOS = checkImportCost(file);
        //处理导入数据
        batchHandleImportCost(costChangeRecordExpDTOS);
        log.info("本次导入订单费用耗时{}秒",stopwatch.elapsed(TimeUnit.SECONDS));
    }

    public List<ImportCostChangeRecordExpDTO> checkImportCost(MultipartFile file) {
        List<ImportExpOrderCostExcel> dataList;
        try {
            dataList = ExcelUtil.readExcel(file, ImportExpOrderCostExcel.class, 1);
        } catch (Exception e) {
            log.error("读取导入的excel文件异常",e);
            throw new ServiceException("导入数据异常，请仔细检查您的导入文件是否符合模板要求");
        }
        TsfPreconditions.checkArgument(!CollectionUtils.isEmpty(dataList),new ServiceException("本次没有可以导入的数据"));
        Map<String,ExpenseSubject> expenseSubjectNameMap = expenseSubjectService.allActive().stream().collect(Collectors.toMap(ExpenseSubject::getName, Function.identity()));
        List<ImportCostChangeRecordExpDTO> costChangeRecordExpDTOS = Lists.newArrayListWithExpectedSize(dataList.size());
        for(int i=0;i<dataList.size();i++) {
            ImportExpOrderCostExcel importExpOrderCostExcel = dataList.get(i);
            int contentRowNo = i + 2;
            ValidatorUtils.validateEntity(importExpOrderCostExcel, contentRowNo);
            importExpOrderCostExcel.setCostAmount(importExpOrderCostExcel.getCostAmount().replace(",","").replace("，",""));
            //检查费用科目
            ExpenseSubject expenseSubject = expenseSubjectNameMap.get(importExpOrderCostExcel.getExpenseSubjectName());
            Optional.ofNullable(expenseSubject).orElseThrow(()->new ServiceException(StrUtil.format("第{}行费用科目错误",contentRowNo)));
            //检查应收金额
            TsfPreconditions.checkArgument(StringUtils.isBigDecimal(importExpOrderCostExcel.getCostAmount()),new ServiceException(StrUtil.format("第{}行费用金额{}格式错误",contentRowNo,importExpOrderCostExcel.getCostAmount())));
            BigDecimal costAmount = new BigDecimal(importExpOrderCostExcel.getCostAmount());
            if(costAmount.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException(StrUtil.format("第{}行费用金额{}小于0",contentRowNo,importExpOrderCostExcel.getCostAmount()));
            }
            CollectionSourceEnum collectionSourceEnum = CollectionSourceEnum.ofName(importExpOrderCostExcel.getCollectionSource());
            if("A0001".equals(expenseSubject.getId()) && !Objects.equals(collectionSourceEnum,CollectionSourceEnum.DEDUCTION)){
                throw new ServiceException(StrUtil.format("订单{}代理费支付方式必须为【{}】",importExpOrderCostExcel.getExpOrderNo(),CollectionSourceEnum.DEDUCTION.getName()));
            }
            ImportCostChangeRecordExpDTO costChangeRecordExpDTO = new ImportCostChangeRecordExpDTO();
            costChangeRecordExpDTO.setExpOrderNo(importExpOrderCostExcel.getExpOrderNo());
            costChangeRecordExpDTO.setCollectionSource(collectionSourceEnum.getCode());
            costChangeRecordExpDTO.setExpenseSubjectId(expenseSubject.getId());
            costChangeRecordExpDTO.setCostAmount(costAmount);
            costChangeRecordExpDTO.setMemo("导入费用" + (StringUtils.isNotEmpty(importExpOrderCostExcel.getMemo()) ? "，" + StringUtils.null2EmptyWithTrim(importExpOrderCostExcel.getMemo()) : "") );
            costChangeRecordExpDTOS.add(costChangeRecordExpDTO);
        }
        //检查订单编号+费用科目是否唯一
        Map<String,List<ImportExpOrderCostExcel>> orderNoMap = dataList.stream().collect(Collectors.groupingBy(r->r.getExpOrderNo()));
        orderNoMap.forEach((k,v)->{
            Map<String, Long> expenseSubjectNameCountMap = v.stream().collect(Collectors.groupingBy(ImportExpOrderCostExcel::getExpenseSubjectName,Collectors.counting()));
            expenseSubjectNameCountMap.forEach((expenseSubjectName,num)->{
                TsfPreconditions.checkArgument(num == 1,new ServiceException(StrUtil.format("订单{}存在{}条费用科目为{}的数据，请更正数据后再导入",k,num,expenseSubjectName)));
            });
        });
        return costChangeRecordExpDTOS;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchHandleImportCost(List<ImportCostChangeRecordExpDTO> dataList) {
        //每一次导入当做一个事务锁，防止出现账务数据错乱
        String lockKey = DistributedLockEnum.ORDER_COST_IMPORT.getCode();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            //由于处理时间长，此处锁时间设置为30秒
            isLock = lock.tryLock(30, TimeUnit.SECONDS);
            if (!isLock) {
                log.error("未获取到锁，出口导入订单费用");
                throw new ServiceException("其他人正在导入费用数据，请稍后再试");
            }
            //获取所有已启用的费用科目信息，key为费用科目id（读缓存）
            Map<String,ExpenseSubject> expenseSubjectIdMap = expenseSubjectService.allActive().stream().collect(Collectors.toMap(ExpenseSubject::getId, Function.identity()));
            //由于导入的订单号会重复多个，此处存入集合中不再重复查询
            Map<String,ExpOrder> expOrderMap = Maps.newHashMap();
            //新增的订单费用记录
            List<ExpOrderCost> expOrderCosts = Lists.newArrayList();
            //新增订单变更记录
            List<CostChangeRecordExp> costChangeRecordExps = Lists.newArrayList();
            //需要核销的订单id
            Map<Long,ExpOrder> writeOffExpOrderMap = Maps.newHashMap();
            //批量根据订单号查询订单费用信息
            List<String> expOrderNos = dataList.stream().map(ImportCostChangeRecordExpDTO::getExpOrderNo).distinct().collect(Collectors.toList());
            List<ExpOrderCost> expOrderCostList = expOrderCostService.findByExpOrderNoBatch(expOrderNos);
            //根据订单id把所有费用信息按订单id分组
            Map<Long,List<ExpOrderCost>> expOrderCostMap = expOrderCostList.stream().collect(Collectors.groupingBy(ExpOrderCost::getExpOrderId));
            for(ImportCostChangeRecordExpDTO dto : dataList) {
                ExpenseSubject expenseSubject = expenseSubjectIdMap.get(dto.getExpenseSubjectId());
                Optional.ofNullable(expenseSubject).orElseThrow(()->new ServiceException("费用科目错误"));
                ExpOrder expOrder = expOrderMap.computeIfAbsent(dto.getExpOrderNo(),expOrderNo->expOrderService.findByDocNo(expOrderNo));
                Optional.ofNullable(expOrder).orElseThrow(()->new ServiceException(StrUtil.format("订单号{}错误",dto.getExpOrderNo())));
                TsfPreconditions.checkArgument(StringUtils.isEmpty(expOrder.getPurchaseContractNo()),new ServiceException(StrUtil.format("订单{}已经生成采购合同，不允许调整费用",dto.getExpOrderNo())));
                BigDecimal originCostAmount = BigDecimal.ZERO;
                //可能存在某些订单没有费用信息
                List<ExpOrderCost> expOrderCostsGroup = expOrderCostMap.getOrDefault(expOrder.getId(),Lists.newArrayList());
                Map<String,ExpOrderCost> expOrderCostExpenseSubjectMap = expOrderCostsGroup.stream().collect(Collectors.toMap(ExpOrderCost::getExpenseSubjectId,Function.identity()));
                ExpOrderCost expOrderCost = expOrderCostExpenseSubjectMap.get(expenseSubject.getId());
                //判断订单号+费用科目是否存在订单费用
                //ExpOrderCost expOrderCost = expOrderCostService.findByExpOrderNoAndExpenseSubjectId(dto.getExpOrderNo(),expenseSubject.getId());
                if(Objects.nonNull(expOrderCost)) {
                   //修改费用
                   //如果费用支付方式+费用金额没有发生变更，则不操作
                    if(expOrderCost.getCollectionSource().equals(dto.getCollectionSource()) && expOrderCost.getReceAmount().compareTo(dto.getCostAmount()) == 0) {
                        log.info("订单费用导入，订单【{}】，费用科目【{}】费用没有变更，不报错，忽略本次操作",dto.getExpOrderNo(),expenseSubject.getName());
                        continue;
                    }
                    TsfPreconditions.checkArgument(Objects.equals(expOrderCost.getIsAllowEdit(),Boolean.TRUE),new ServiceException(StrUtil.format("订单{}费用【{}】不允许修改",dto.getExpOrderNo(),expenseSubject.getName())));
                    // 检查原支付方式是否(客户单独支付)
                    if(Objects.equals(CollectionSourceEnum.PAYMENT,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ||
                            Objects.equals(CollectionSourceEnum.OTHER,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ){
                        // 检查费用是否核销
                        if(expOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO) == 1){
                            //取消核销
                            writeOffService.cancelWriteOffByExpOrderCost(expOrderCost);
                        }
                        // 处理交易流水
                        if(Objects.equals(Boolean.TRUE,expOrderCost.getIsLock())) {
                            TransactionFlowDTO param = new TransactionFlowDTO();
                            param.setCustomerId(expOrder.getCustomerId());
                            param.setTransactionType(TransactionTypeEnum.EXP_ORDER.getCode());
                            param.setTransactionNo(expOrder.getDocNo());
                            param.setMemo(String.format("出口费用调整取消：%s[%s]",expenseSubject.getName(),expOrderCost.getReceAmount()));
                            param.setAmount(expOrderCost.getReceAmount());
                            param.setTransactionCategory(TransactionCategoryEnum.INCOME.getCode());
                            transactionFlowService.recordTransactionFlow(param);
                        }
                    }

                    //获取订单修改前费用
                    originCostAmount = expOrderCost.getReceAmount();
                    expOrderCost.setActualAmount(originCostAmount);
                    //应收金额
                    expOrderCost.setReceAmount(dto.getCostAmount());
                    expOrderCost.setMemo(dto.getMemo());
                    //防止系统重复计算
                    expOrderCost.setIsAutomatic(Boolean.FALSE);
                    expOrderCost.setOperator(SecurityUtil.getCurrentPersonName());
                    //计算账期
                    expOrderCost.setHappenDate(expOrder.getOrderDate());
                    expOrderCost.setPeriodDate(expOrder.getOrderDate());
                    expOrderCost.setLateFeeDate(expOrder.getOrderDate());
                    expOrderCost.setOverdueRate(BigDecimal.ZERO);
                    // 支付方式
                    expOrderCost.setCollectionSource(dto.getCollectionSource());
                    expOrderCostService.updateById(expOrderCost);

                } else {
                    // 新增费用
                    expOrderCost = new ExpOrderCost();
                    expOrderCost.setExpenseSubjectId(expenseSubject.getId());
                    expOrderCost.setExpOrderId(expOrder.getId());
                    expOrderCost.setCostClassify(ExpenseSubjectUtil.getCostClassify(expenseSubject.getId()));
                    expOrderCost.setIsAutomatic(Boolean.FALSE);
                    expOrderCost.setIsFirstWriteOff(Boolean.FALSE);
                    expOrderCost.setIsLock(expOrder.getIsExp());
                    //计算账期
                    expOrderCost.setHappenDate(expOrder.getOrderDate());
                    expOrderCost.setPeriodDate(expOrder.getOrderDate());
                    expOrderCost.setLateFeeDate(expOrder.getOrderDate());
                    expOrderCost.setOverdueRate(BigDecimal.ZERO);
                    expOrderCost.setMark(expOrder.getDocNo());
                    expOrderCost.setOperator(SecurityUtil.getCurrentPersonName());
                    expOrderCost.setIsAllowEdit(Boolean.TRUE);//人工新增或修改的可以修改
                    //原始费用
                    expOrderCost.setActualAmount(BigDecimal.ZERO);
                    //应收金额
                    expOrderCost.setReceAmount(dto.getCostAmount());
                    //已收金额
                    expOrderCost.setAcceAmount(BigDecimal.ZERO);
                    expOrderCost.setMemo(dto.getMemo());
                    // 支付方式
                    expOrderCost.setCollectionSource(dto.getCollectionSource());
                    expOrderCost.setId(snowflake.nextId());
                    expOrderCosts.add(expOrderCost);
                }

                //登记费用调整记录
                CostChangeRecordExp costChangeRecord = new CostChangeRecordExp();
                costChangeRecord.setId(snowflake.nextId());
                costChangeRecord.setExpOrderId(expOrder.getId());
                costChangeRecord.setExpOrderNo(expOrder.getDocNo());
                costChangeRecord.setCustomerId(expOrder.getCustomerId());
                costChangeRecord.setExpenseSubjectId(expenseSubject.getId());
                costChangeRecord.setExpenseSubjectName(expenseSubject.getName());
                costChangeRecord.setOriginCostAmount(originCostAmount);
                costChangeRecord.setCostAmount(expOrderCost.getReceAmount());
                costChangeRecord.setMemo(expOrderCost.getMemo());
                costChangeRecord.setDateCreated(LocalDateTime.now());
                costChangeRecord.setCreateBy(SecurityUtil.getCurrentPersonName());
                costChangeRecord.setDateUpdated(LocalDateTime.now());
                costChangeRecord.setUpdateBy(SecurityUtil.getCurrentPersonName());
                costChangeRecordExps.add(costChangeRecord);

                // 修改后支付方式是否(客户单独支付)
                if(Objects.equals(CollectionSourceEnum.PAYMENT,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ||
                        Objects.equals(CollectionSourceEnum.OTHER,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ){
                    // 处理交易流水
                    if(Objects.equals(Boolean.TRUE,expOrderCost.getIsLock())) {
                        TransactionFlowDTO param = new TransactionFlowDTO();
                        param.setCustomerId(expOrder.getCustomerId());
                        param.setTransactionType(TransactionTypeEnum.EXP_ORDER.getCode());
                        param.setTransactionNo(expOrder.getDocNo());
                        param.setMemo(String.format("出口费用调整增加：%s[%s]",expenseSubject.getName(),expOrderCost.getReceAmount()));
                        param.setAmount(expOrderCost.getReceAmount());
                        param.setTransactionCategory(TransactionCategoryEnum.OUTCOME.getCode());
                        transactionFlowService.recordTransactionFlow(param);

                        //执行订单费用核销
                        writeOffExpOrderMap.putIfAbsent(expOrder.getId(), expOrder);
                    }
                }
            }
            //批量新增订单费用
            if(CollUtil.isNotEmpty(expOrderCosts)) {
                expOrderCostService.savaBatch(expOrderCosts);
            }
            //批量新增订单费用变更记录
            if(CollUtil.isNotEmpty(costChangeRecordExps)) {
                super.savaBatch(costChangeRecordExps);
            }
            //执行订单费用核销，按订单
            if(CollUtil.isNotEmpty(writeOffExpOrderMap)) {
                writeOffExpOrderMap.forEach((orderId,expOrder)->{
                    writeOffService.writeOffExpOrderId(orderId);
                });
            }
        }  catch (InterruptedException e) {
            log.error("费用调整获取锁异常",e);
            throw new ServiceException("服务拥挤，请稍后再试");
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importOrderOtherCost(MultipartFile file) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        //验证导入数据是否符合规范
        List<ImportCostChangeRecordExpDTO> costChangeRecordExpDTOS = checkImportOtherCost(file);
        //处理导入数据
        batchHandleImportOtherCost(costChangeRecordExpDTOS);
        log.info("本次导入订单费用耗时{}秒",stopwatch.elapsed(TimeUnit.SECONDS));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchHandleImportOtherCost(List<ImportCostChangeRecordExpDTO> dataList) {
        //每一次导入当做一个事务锁，防止出现账务数据错乱
        String lockKey = DistributedLockEnum.ORDER_COST_IMPORT.getCode();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            //由于处理时间长，此处锁时间设置为30秒
            isLock = lock.tryLock(30, TimeUnit.SECONDS);
            if (!isLock) {
                log.error("未获取到锁，出口导入订单费用");
                throw new ServiceException("其他人正在导入费用数据，请稍后再试");
            }
            //获取所有已启用的费用科目信息，key为费用科目id（读缓存）
            Map<String,ExpenseSubject> expenseSubjectIdMap = expenseSubjectService.allActive().stream().collect(Collectors.toMap(ExpenseSubject::getId, Function.identity()));
            //由于导入的订单号会重复多个，此处存入集合中不再重复查询
            Map<String,ExpOrder> expOrderMap = Maps.newHashMap();
            //新增的订单费用记录
            List<ExpOrderCost> expOrderCosts = Lists.newArrayList();
            //新增订单变更记录
            List<CostChangeRecordExp> costChangeRecordExps = Lists.newArrayList();
            //需要核销的订单id
            Map<Long,ExpOrder> writeOffExpOrderMap = Maps.newHashMap();
            //批量根据订单号查询订单费用信息
            List<String> expOrderNos = dataList.stream().map(ImportCostChangeRecordExpDTO::getExpOrderNo).distinct().collect(Collectors.toList());
            List<ExpOrderCost> expOrderCostList = expOrderCostService.findByExpOrderNoBatch(expOrderNos);
            //根据订单id把所有费用信息按订单id分组
            Map<Long,List<ExpOrderCost>> expOrderCostMap = expOrderCostList.stream().collect(Collectors.groupingBy(ExpOrderCost::getExpOrderId));
            for(ImportCostChangeRecordExpDTO dto : dataList) {
                ExpenseSubject expenseSubject = expenseSubjectIdMap.get(dto.getExpenseSubjectId());
                Optional.ofNullable(expenseSubject).orElseThrow(()->new ServiceException("费用科目错误"));
                ExpOrder expOrder = expOrderMap.computeIfAbsent(dto.getExpOrderNo(),expOrderNo->expOrderService.findByDocNo(expOrderNo));
                Optional.ofNullable(expOrder).orElseThrow(()->new ServiceException(StrUtil.format("订单号{}错误",dto.getExpOrderNo())));
                TsfPreconditions.checkArgument(StringUtils.isEmpty(expOrder.getPurchaseContractNo()),new ServiceException(StrUtil.format("订单{}已经生成采购合同，不允许调整费用",dto.getExpOrderNo())));
                BigDecimal originCostAmount = BigDecimal.ZERO;
                //可能存在某些订单没有费用信息
                List<ExpOrderCost> expOrderCostsGroup = expOrderCostMap.getOrDefault(expOrder.getId(),Lists.newArrayList());
                Map<String,ExpOrderCost> expOrderCostExpenseSubjectMap = expOrderCostsGroup.stream().collect(Collectors.toMap(ExpOrderCost::getExpenseSubjectId,Function.identity()));
                ExpOrderCost expOrderCost = expOrderCostExpenseSubjectMap.get(expenseSubject.getId());
                //判断订单号+费用科目是否存在订单费用
                //ExpOrderCost expOrderCost = expOrderCostService.findByExpOrderNoAndExpenseSubjectId(dto.getExpOrderNo(),expenseSubject.getId());
                if(Objects.nonNull(expOrderCost)) {
                    //修改费用
                    //如果费用支付方式+费用金额没有发生变更，则不操作
                    if(expOrderCost.getCollectionSource().equals(dto.getCollectionSource()) && expOrderCost.getReceAmount().compareTo(dto.getCostAmount()) == 0) {
                        log.info("订单费用导入，订单【{}】，费用科目【{}】费用没有变更，不报错，忽略本次操作",dto.getExpOrderNo(),expenseSubject.getName());
                        continue;
                    }
                    TsfPreconditions.checkArgument(Objects.equals(expOrderCost.getIsAllowEdit(),Boolean.TRUE),new ServiceException(StrUtil.format("订单{}费用【{}】不允许修改",dto.getExpOrderNo(),expenseSubject.getName())));
                    // 检查原支付方式是否(客户单独支付)
                    if(Objects.equals(CollectionSourceEnum.PAYMENT,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ||
                            Objects.equals(CollectionSourceEnum.OTHER,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ){
                        // 检查费用是否核销
                        if(expOrderCost.getAcceAmount().compareTo(BigDecimal.ZERO) == 1){
                            //取消核销
                            writeOffService.cancelWriteOffByExpOrderCost(expOrderCost);
                        }
                        // 处理交易流水
                        if(Objects.equals(Boolean.TRUE,expOrderCost.getIsLock())) {
                            TransactionFlowDTO param = new TransactionFlowDTO();
                            param.setCustomerId(expOrder.getCustomerId());
                            param.setTransactionType(TransactionTypeEnum.EXP_ORDER.getCode());
                            param.setTransactionNo(expOrder.getDocNo());
                            param.setMemo(String.format("出口费用调整取消：%s[%s]",expenseSubject.getName(),expOrderCost.getReceAmount()));
                            param.setAmount(expOrderCost.getReceAmount());
                            param.setTransactionCategory(TransactionCategoryEnum.INCOME.getCode());
                            transactionFlowService.recordTransactionFlow(param);
                        }
                    }

                    //获取订单修改前费用
                    originCostAmount = expOrderCost.getReceAmount();
                    expOrderCost.setActualAmount(originCostAmount);
                    //应收金额
                    expOrderCost.setReceAmount(dto.getCostAmount());
                    //已收金额
                    expOrderCost.setAcceAmount(Objects.isNull(dto.getAcceAmount()) ? BigDecimal.ZERO : dto.getAcceAmount());
                    expOrderCost.setMemo(dto.getMemo());
                    //防止系统重复计算
                    expOrderCost.setIsAutomatic(Boolean.FALSE);
                    expOrderCost.setOperator(SecurityUtil.getCurrentPersonName());
                    //计算账期  如果有日期则使用导入的日期
                    if(Objects.nonNull(dto.getHappenDate())) {
                        expOrderCost.setHappenDate(dto.getHappenDate());
                        expOrderCost.setPeriodDate(dto.getHappenDate());
                        expOrderCost.setLateFeeDate(dto.getHappenDate());
                    } else {
                        expOrderCost.setHappenDate(expOrder.getOrderDate());
                        expOrderCost.setPeriodDate(expOrder.getOrderDate());
                        expOrderCost.setLateFeeDate(expOrder.getOrderDate());
                    }
                    expOrderCost.setOverdueRate(BigDecimal.ZERO);
                    // 支付方式
                    expOrderCost.setCollectionSource(dto.getCollectionSource());
                    // 应付金额
                    expOrderCost.setPayAmount(dto.getPayAmount());
                    // 应付币制
                    expOrderCost.setPayCurrencyCode(dto.getPayCurrencyCode());
                    // 供应商
                    expOrderCost.setExtend1(StringUtils.null2EmptyWithTrim(dto.getExtend1()));
                    expOrderCostService.updateById(expOrderCost);

                } else {
                    // 新增费用
                    expOrderCost = new ExpOrderCost();
                    expOrderCost.setExpenseSubjectId(expenseSubject.getId());
                    expOrderCost.setExpOrderId(expOrder.getId());
                    expOrderCost.setCostClassify(ExpenseSubjectUtil.getCostClassify(expenseSubject.getId()));
                    expOrderCost.setIsAutomatic(Boolean.FALSE);
                    expOrderCost.setIsFirstWriteOff(Boolean.FALSE);
                    expOrderCost.setIsLock(expOrder.getIsExp());
                    //计算账期  如果有日期则使用导入的日期
                    if(Objects.nonNull(dto.getHappenDate())) {
                        expOrderCost.setHappenDate(dto.getHappenDate());
                        expOrderCost.setPeriodDate(dto.getHappenDate());
                        expOrderCost.setLateFeeDate(dto.getHappenDate());
                    } else {
                        expOrderCost.setHappenDate(expOrder.getOrderDate());
                        expOrderCost.setPeriodDate(expOrder.getOrderDate());
                        expOrderCost.setLateFeeDate(expOrder.getOrderDate());
                    }
                    expOrderCost.setOverdueRate(BigDecimal.ZERO);
                    expOrderCost.setMark(expOrder.getDocNo());
                    expOrderCost.setOperator(SecurityUtil.getCurrentPersonName());
                    expOrderCost.setIsAllowEdit(Boolean.TRUE);//人工新增或修改的可以修改
                    //原始费用
                    expOrderCost.setActualAmount(BigDecimal.ZERO);
                    //应收金额
                    expOrderCost.setReceAmount(dto.getCostAmount());
                    //已收金额
                    expOrderCost.setAcceAmount(Objects.isNull(dto.getAcceAmount()) ? BigDecimal.ZERO : dto.getAcceAmount());
                    expOrderCost.setMemo(dto.getMemo());
                    // 支付方式
                    expOrderCost.setCollectionSource(dto.getCollectionSource());
                    // 应付金额
                    expOrderCost.setPayAmount(dto.getPayAmount());
                    // 应付币制
                    expOrderCost.setPayCurrencyCode(dto.getPayCurrencyCode());
                    // 供应商
                    expOrderCost.setExtend1(StringUtils.null2EmptyWithTrim(dto.getExtend1()));
                    expOrderCost.setId(snowflake.nextId());
                    expOrderCosts.add(expOrderCost);
                }

                //登记费用调整记录
                CostChangeRecordExp costChangeRecord = new CostChangeRecordExp();
                costChangeRecord.setId(snowflake.nextId());
                costChangeRecord.setExpOrderId(expOrder.getId());
                costChangeRecord.setExpOrderNo(expOrder.getDocNo());
                costChangeRecord.setCustomerId(expOrder.getCustomerId());
                costChangeRecord.setExpenseSubjectId(expenseSubject.getId());
                costChangeRecord.setExpenseSubjectName(expenseSubject.getName());
                costChangeRecord.setOriginCostAmount(originCostAmount);
                costChangeRecord.setCostAmount(expOrderCost.getReceAmount());
                costChangeRecord.setMemo(expOrderCost.getMemo());
                costChangeRecord.setDateCreated(LocalDateTime.now());
                costChangeRecord.setCreateBy(SecurityUtil.getCurrentPersonName());
                costChangeRecord.setDateUpdated(LocalDateTime.now());
                costChangeRecord.setUpdateBy(SecurityUtil.getCurrentPersonName());
                costChangeRecordExps.add(costChangeRecord);

                // 修改后支付方式是否(客户单独支付)
                if(Objects.equals(CollectionSourceEnum.PAYMENT,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ||
                        Objects.equals(CollectionSourceEnum.OTHER,CollectionSourceEnum.of(expOrderCost.getCollectionSource())) ){
                    // 处理交易流水
                    if(Objects.equals(Boolean.TRUE,expOrderCost.getIsLock())) {
                        TransactionFlowDTO param = new TransactionFlowDTO();
                        param.setCustomerId(expOrder.getCustomerId());
                        param.setTransactionType(TransactionTypeEnum.EXP_ORDER.getCode());
                        param.setTransactionNo(expOrder.getDocNo());
                        param.setMemo(String.format("出口费用调整增加：%s[%s]",expenseSubject.getName(),expOrderCost.getReceAmount()));
                        param.setAmount(expOrderCost.getReceAmount());
                        param.setTransactionCategory(TransactionCategoryEnum.OUTCOME.getCode());
                        transactionFlowService.recordTransactionFlow(param);

                        //执行订单费用核销
                        writeOffExpOrderMap.putIfAbsent(expOrder.getId(), expOrder);
                    }
                }
            }
            //批量新增订单费用
            if(CollUtil.isNotEmpty(expOrderCosts)) {
                expOrderCostService.savaBatch(expOrderCosts);
            }
            //批量新增订单费用变更记录
            if(CollUtil.isNotEmpty(costChangeRecordExps)) {
                super.savaBatch(costChangeRecordExps);
            }
            //执行订单费用核销，按订单
            if(CollUtil.isNotEmpty(writeOffExpOrderMap)) {
                writeOffExpOrderMap.forEach((orderId,expOrder)->{
                    writeOffService.writeOffExpOrderId(orderId);
                });
            }
        }  catch (InterruptedException e) {
            log.error("费用调整获取锁异常",e);
            throw new ServiceException("服务拥挤，请稍后再试");
        } finally {
            //释放锁
            lock.unlock();
        }
    }


    public List<ImportCostChangeRecordExpDTO> checkImportOtherCost(MultipartFile file) {
        List<ImportExpOrderOtherCostExcel> dataList;
        try {
            dataList = ExcelUtil.readExcel(file, ImportExpOrderOtherCostExcel.class, 1);
        } catch (Exception e) {
            log.error("读取导入的excel文件异常",e);
            throw new ServiceException("导入数据异常，请仔细检查您的导入文件是否符合模板要求");
        }
        TsfPreconditions.checkArgument(!CollectionUtils.isEmpty(dataList),new ServiceException("本次没有可以导入的数据"));
        Map<String,CurrencyVO> currencyMap = currencyService.select().stream().collect(Collectors.toMap(CurrencyVO::getName,Function.identity()));
        Map<String,ExpenseSubject> expenseSubjectNameMap = expenseSubjectService.allActive().stream().collect(Collectors.toMap(ExpenseSubject::getName, Function.identity()));
        List<ImportCostChangeRecordExpDTO> costChangeRecordExpDTOS = Lists.newArrayListWithExpectedSize(dataList.size());
        for(int i=1;i<dataList.size();i++) {
            ImportExpOrderOtherCostExcel importExpOrderOtherCostExcel = dataList.get(i);
            int contentRowNo = i + 2;
            ValidatorUtils.validateEntity(importExpOrderOtherCostExcel, contentRowNo);
            importExpOrderOtherCostExcel.setCostAmount(StringUtils.null2EmptyWithTrim(importExpOrderOtherCostExcel.getCostAmount()).replace(",","").replace("，",""));
            importExpOrderOtherCostExcel.setReceivedAmount(StringUtils.null2EmptyWithTrim(importExpOrderOtherCostExcel.getReceivedAmount()).replace(",","").replace("，",""));
            importExpOrderOtherCostExcel.setNeedPayAmount(StringUtils.null2EmptyWithTrim(importExpOrderOtherCostExcel.getNeedPayAmount()).replace(",","").replace("，",""));
            //检查费用科目
            ExpenseSubject expenseSubject = expenseSubjectNameMap.get(importExpOrderOtherCostExcel.getExpenseSubjectName());
            Optional.ofNullable(expenseSubject).orElseThrow(()->new ServiceException(StrUtil.format("第{}行费用科目【{}】错误或者系统未配置",contentRowNo,importExpOrderOtherCostExcel.getExpenseSubjectName())));
            //检查币制
            CurrencyVO currency = currencyMap.get(importExpOrderOtherCostExcel.getCurrencyName());
            Optional.ofNullable(currency).orElseThrow(()->new ServiceException(StrUtil.format("第{}行币制【{}】错误或系统未配置",contentRowNo,importExpOrderOtherCostExcel.getCurrencyName())));
            //检查应收金额
            TsfPreconditions.checkArgument(StringUtils.isBigDecimal(importExpOrderOtherCostExcel.getCostAmount()),new ServiceException(StrUtil.format("第{}行应收金额{}格式错误",contentRowNo,importExpOrderOtherCostExcel.getCostAmount())));
            BigDecimal costAmount = new BigDecimal(importExpOrderOtherCostExcel.getCostAmount());
            if(costAmount.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException(StrUtil.format("第{}行应收金额{}小于0",contentRowNo,importExpOrderOtherCostExcel.getCostAmount()));
            }
            //检查已收金额
            if(StringUtils.isNotEmpty(importExpOrderOtherCostExcel.getReceivedAmount())) {
                TsfPreconditions.checkArgument(StringUtils.isBigDecimal(importExpOrderOtherCostExcel.getReceivedAmount()),new ServiceException(StrUtil.format("第{}行已收金额{}格式错误",contentRowNo,importExpOrderOtherCostExcel.getReceivedAmount())));
                BigDecimal receivedAmount = new BigDecimal(importExpOrderOtherCostExcel.getReceivedAmount());
                if(receivedAmount.compareTo(BigDecimal.ZERO) < 0) {
                    throw new ServiceException(StrUtil.format("第{}行已收金额{}小于0",contentRowNo,importExpOrderOtherCostExcel.getCostAmount()));
                }
            }
            //检查应付金额
            if(StringUtils.isNotEmpty(importExpOrderOtherCostExcel.getNeedPayAmount())) {
                TsfPreconditions.checkArgument(StringUtils.isBigDecimal(importExpOrderOtherCostExcel.getNeedPayAmount()),new ServiceException(StrUtil.format("第{}行应付金额{}格式错误",contentRowNo,importExpOrderOtherCostExcel.getNeedPayAmount())));
                BigDecimal needPayAmount = new BigDecimal(importExpOrderOtherCostExcel.getNeedPayAmount());
                if(needPayAmount.compareTo(BigDecimal.ZERO) < 0) {
                    throw new ServiceException(StrUtil.format("第{}行应付金额{}小于0",contentRowNo,importExpOrderOtherCostExcel.getNeedPayAmount()));
                }
            }
            CollectionSourceEnum collectionSourceEnum = CollectionSourceEnum.ofName(importExpOrderOtherCostExcel.getCollectionSource());
            if("A0001".equals(expenseSubject.getId()) && !Objects.equals(collectionSourceEnum,CollectionSourceEnum.DEDUCTION)){
                throw new ServiceException(StrUtil.format("订单{}代理费支付方式必须为【{}】",importExpOrderOtherCostExcel.getExpOrderNo(),CollectionSourceEnum.DEDUCTION.getName()));
            }
            ImportCostChangeRecordExpDTO costChangeRecordExpDTO = new ImportCostChangeRecordExpDTO();
            costChangeRecordExpDTO.setExpOrderNo(importExpOrderOtherCostExcel.getExpOrderNo());
            costChangeRecordExpDTO.setCollectionSource(collectionSourceEnum.getCode());
            costChangeRecordExpDTO.setExpenseSubjectId(expenseSubject.getId());
            costChangeRecordExpDTO.setCostAmount(costAmount);
            if(StringUtils.isNotEmpty(importExpOrderOtherCostExcel.getReceivedAmount())) {
                costChangeRecordExpDTO.setAcceAmount(new BigDecimal(importExpOrderOtherCostExcel.getReceivedAmount()));
            }
            if(StringUtils.isNotEmpty(importExpOrderOtherCostExcel.getNeedPayAmount())) {
                costChangeRecordExpDTO.setPayAmount(new BigDecimal(importExpOrderOtherCostExcel.getNeedPayAmount()));
            }
            costChangeRecordExpDTO.setPayCurrencyCode(currencyMap.get(importExpOrderOtherCostExcel.getCurrencyName()).getId());
            costChangeRecordExpDTO.setExtend1(importExpOrderOtherCostExcel.getSupplierName());
            if(StringUtils.isNotEmpty(importExpOrderOtherCostExcel.getCostDate())) {
                costChangeRecordExpDTO.setHappenDate(LocalDateTimeUtils.parse(importExpOrderOtherCostExcel.getCostDate().replace("/","").replace("-","") + "000000","yyyyMMddHHmmss"));
            }
            costChangeRecordExpDTO.setMemo("导入其他费用" + (StringUtils.isNotEmpty(importExpOrderOtherCostExcel.getMemo()) ? "，" + StringUtils.null2EmptyWithTrim(importExpOrderOtherCostExcel.getMemo()) : "") );
            costChangeRecordExpDTOS.add(costChangeRecordExpDTO);
        }
        //检查订单编号+费用科目是否唯一
        Map<String,List<ImportExpOrderOtherCostExcel>> orderNoMap = dataList.stream().collect(Collectors.groupingBy(r->r.getExpOrderNo()));
        orderNoMap.forEach((k,v)->{
            Map<String, Long> expenseSubjectNameCountMap = v.stream().collect(Collectors.groupingBy(ImportExpOrderOtherCostExcel::getExpenseSubjectName,Collectors.counting()));
            expenseSubjectNameCountMap.forEach((expenseSubjectName,num)->{
                TsfPreconditions.checkArgument(num == 1,new ServiceException(StrUtil.format("订单{}存在{}条费用科目为{}的数据，请更正数据后再导入",k,num,expenseSubjectName)));
            });
        });
        return costChangeRecordExpDTOS;
    }


}
